
// ===============================================================================================================
// -*- C++ -*-
//
// Array.inl - Dynamic array template class.
//
// Copyright (c) 2010 Guilherme R. Lampert
// guilherme.ronaldo.lampert@gmail.com
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// ===============================================================================================================

#ifndef __ARRAY_INL__
#define __ARRAY_INL__

// =====================================================
// Template Class Array
// A STL vector-like class with some extra functionality
// =====================================================

template<class _X> class Array {

public:

	typedef size_t size_type;
	typedef _X value_type;

	typedef _X& reference;
	typedef const _X& const_reference;

	typedef _X* pointer;
	typedef const _X* const_pointer;

public:

	Array();
	Array(size_type count);
	Array(size_type count, const value_type & val);

	void resize(size_type count);
	void push_back(const value_type & val);
	void pop_back();
	int  add_unique(const value_type & val);
	int  contains(const value_type & val) const;
	void fill(const value_type & val);
	void remove(const value_type & val);
	void erase(size_type index);
	void clear();

	~Array();

public:

	size_type size() const
	{
		return (_array_size);
	}

	bool empty() const
	{
		return (_array_size == 0);
	}

	reference at(size_type index)
	{
		return (_ptr[index]);
	}

	const_reference at(size_type index) const
	{
		return (_ptr[index]);
	}

	reference operator[] (size_type index)
	{
		return (_ptr[index]);
	}

	const_reference operator[] (size_type index) const
	{
		return (_ptr[index]);
	}

	pointer data()
	{
		return (_ptr);
	}

	const_pointer data() const
	{
		return (_ptr);
	}

private:

	value_type * _ptr;
	size_type _ptr_size;
	size_type _array_size;

	// Elements per allocation (Powers of 2):
	enum { ALLOC_SIZE = ( (sizeof(_X) <= 1) ? 32 \
	                    : (sizeof(_X) <= 2) ? 16 \
	                    : (sizeof(_X) <= 4) ? 8  \
	                    : (sizeof(_X) <= 8) ? 4 : 2 ) };
};

// == Class Array ===============================================================================================================

template<class _X>
	Array<_X>::Array()
		: _ptr(0), _ptr_size(0), _array_size(0)
{
}

template<class _X>
	Array<_X>::Array(size_type count)
		: _ptr(0), _ptr_size(0), _array_size(0)
{
	resize(count);
}

template<class _X>
	Array<_X>::Array(size_type count, const value_type & val)
		: _ptr(0), _ptr_size(0), _array_size(0)
{
	resize(count);
	fill(val);
}

template<class _X>
	void Array<_X>::resize(size_type count)
{
	if ((count > _ptr_size) && (count > 0))
	{
		value_type * _old = _ptr;

		_ptr_size = count;
		_ptr = new _X [_ptr_size];

		if (_old != 0)
		{
			for (size_type i = 0; i < _array_size; i++)
				_ptr[i] = _old[i];

			delete[] _old;
		}

		_array_size = count;
	}
}

template<class _X>
	void Array<_X>::push_back(const value_type & val)
{
	if (_array_size == _ptr_size)
	{
		size_type old_size = _array_size;

		resize(_ptr_size + ALLOC_SIZE);

		_array_size = old_size;
	}

	_ptr[_array_size++] = val;
}

template<class _X>
	void Array<_X>::pop_back()
{
	if (_array_size > 0)
		_array_size--;
}

template<class _X>
	int Array<_X>::add_unique(const value_type & val)
{
	int res = contains(val);

	if (res == 0)
		push_back(val);

	return (res);
}

template<class _X>
	int Array<_X>::contains(const value_type & val) const
{
	int count = 0;

	for (size_type i = 0; i < _array_size; i++)
	{
		if (_ptr[i] == val)
			count++;
	}

	return (count);
}

template<class _X>
	void Array<_X>::fill(const value_type & val)
{
	for (size_type i = 0; i < _array_size; i++)
		_ptr[i] = val;
}

template<class _X>
	void Array<_X>::remove(const value_type & val)
{
	const value_type element = val;

	for (size_type i = 0; i < _array_size; i++)
	{
		if (_ptr[i] == element)
			erase(i);
	}
}

template<class _X>
	void Array<_X>::erase(size_type index)
{
	if ((_array_size > 0) && (index < _array_size))
	{
		_array_size--;

		while (index < _array_size)
		{
			_ptr[index] = _ptr[index+1];
			index++;
		}
	}
}

template<class _X>
	void Array<_X>::clear()
{
	if (_ptr)
	{
		delete[] _ptr;
		_ptr = 0;

		_ptr_size = 0;
		_array_size = 0;
	}
}

template<class _X>
	Array<_X>::~Array()
{
	if (_ptr)
		delete[] _ptr;
}

#endif // __ARRAY_INL__
